package com.mac.macjetpackdemo.util

import android.app.Application
import android.os.Build
import android.os.Debug
import com.mac.macjetpackdemo.BuildConfig
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*

object CrashHandler : Thread.UncaughtExceptionHandler {

    lateinit var mApp: Application
    private const val OOM = "java.lang.OutOfMemoryError"
    private var path = ""
    fun attach(app: Application) {
        mApp = app
        path = "${mApp.getExternalFilesDir("")
            .toString()}${File.separator}"
    }

    private val mDefaultHandler: Thread.UncaughtExceptionHandler? =
        Thread.getDefaultUncaughtExceptionHandler()

    init {
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    override fun uncaughtException(t: Thread, e: Throwable) {
        e.printStackTrace()
        handleException(e)
        if (isOOM(e)) {
            val memoryPath = "${path}memory${File.separator}"
            val memoryDir = File(memoryPath)
            if (!memoryDir.exists()) {
                memoryDir.mkdirs()
            }
            Debug.dumpHprofData(
                "${memoryPath}dump-${
                SimpleDateFormat.getDateTimeInstance().format(Date())}.hprof"
            )
        }
    }

    private fun isOOM(e: Throwable): Boolean {
        if (OOM == e.javaClass.name) {
            return true
        } else {
            e.cause?.run {
                return isOOM(this)
            }
        }
        return false
    }

    private fun handleException(e: Throwable) {
        val sb = StringBuffer()
        val format = SimpleDateFormat.getDateTimeInstance().format(Date())
        sb.append("\r\n").append(format).append("\r\n")
        sb.append("versionName = ${BuildConfig.VERSION_NAME} \n")
        sb.append("versionCode = ${BuildConfig.VERSION_CODE} \n \n")

        val kClass = Build::class.java
        val declaredFields = kClass.declaredFields
        for (declaredField in declaredFields) {
            declaredField.isAccessible = true
            sb.append("${declaredField.name} = ${declaredField.get(null)?.toString()} \n")
        }

        val writer = StringWriter()
        val printWriter = PrintWriter(writer)
        e.printStackTrace(printWriter)
        var cause = e.cause
        while (cause != null) {
            cause.printStackTrace(printWriter)
            cause = cause.cause
        }
        printWriter.flush()
        printWriter.close()
        writer.flush()
        sb.append(writer.toString())

        val crashPath = "${path}crash${File.separator}"
        val dir = File(crashPath)
        if (!dir.exists()) {
            dir.mkdirs()
        }

        val file = File(
            "${crashPath}crash-${
            SimpleDateFormat.getDateTimeInstance().format(Date())}.log"
        )

        file.writeText(sb.toString())
        writer.close()
    }

}